
/*
排序（对于json中的某个字段进行排序）
删除json中空的字段
函数的防抖
函数的节流
精简的节流函数
    时间戳版
    定时器版
精简的防抖函数
    时间戳版
    定时器版
判断某年份是否为闰年
将日期格式化输出 “2015-08-24”
获得某个月份的天数
将字符串转换为日期
判断两个日期相差的天数
获得N天以后的日期(string/date)
实现一键复制函数
关于句柄的事件方法
保留汉字
字符串截取后面加入...
将一个字符串用给定的字符变成数组
只留下数字
是否是以XX开头
是否已XX结尾
根据数据取得再数组中的索引
移除数组中的某元素
判断元素是否在数组中
删除数组中的某个值并返回新的数组（1）
删除数组中的某个值并返回新的数组（2）
删除数组中的某个值并返回新的数组（3）
数组去重
移除某个字段
正则验证字母和数字
正则验证是否全汉字
返回出敏感词
一键复制
all：布尔全等判断
allEqual：检查数组各项相等
approximatelyEqual：约等于
arrayToCSV：数组转CSV格式（带空格的字符串）
arrayToHtmlList：数组转li列表
average：平均数
averageBy：数组对象属性平均数
bifurcate：拆分断言后的数组
castArray：其它类型转数组
compact：去除数组中的无效/无用值
countOccurrences：检测数值出现次数
deepFlatten：递归扁平化数组
difference：寻找差异（并返回第一个数组独有的）
differenceBy：先执行再寻找差异
dropWhile：删除不符合条件的值
flatten：指定深度扁平化数组
indexOfAll：返回数组中某值的所有索引
intersection：两数组的交集
intersectionWith：两数组都符合条件的交集
intersectionWith：先比较后返回交集
minN：返回指定长度的升序数组
negate：根据条件反向筛选
randomIntArrayInRange：生成两数之间指定长度的随机数组
sample：在指定数组中获取随机数
sampleSize：在指定数组中获取指定长度的随机数
getRandomInt：获取任意两个数之间的随机数例如：(2,5)之间的随机数
shuffle：“洗牌” 数组
nest：根据parent_id生成树结构（阿里一面真题）
attempt：捕获函数运行异常
defer：推迟执行
runPromisesInSeries：运行多个Promises
timeTaken：计算函数执行时间
createEventHub：简单的发布/订阅模式
memoize：缓存函数
once：只调用一次的函数
flattenObject：以键的路径扁平化对象
unflattenObject：以键的路径展开对象
byteSize：返回字符串的字节长度
capitalize：首字母大写
capitalizeEveryWord：每个单词首字母大写
decapitalize：首字母小写
luhnCheck：银行卡号码校验（luhn算法）补充：银行卡号码的校验规则：
splitLines：将多行字符串拆分为行数组。
stripHTMLTags：删除字符串中的HTMl标签
dayOfYear：当前日期天数
forOwn：迭代属性并执行回调
Get Time From Date：返回当前24小时制时间的字符串
Get Days Between Dates：返回日期间的天数
is：检查值是否为特定类型。
isAfterDate：检查是否在某日期后
isBeforeDate：检查是否在某日期前
tomorrow：获取明天的字符串格式时间
equals：全等判断
randomIntegerInRange：生成指定范围的随机整数
randomNumberInRange：生成指定范围的随机小数
round：四舍五入到指定位数
sum：计算数组或多个数字的总和
toCurrency：简单的货币单位转换
bottomVisible：检查页面底部是否可见
Create Directory：检查创建目录
currentURL：返回当前链接url
distance：返回两点间的距离
elementContains：检查是否包含子元素
getStyle：返回指定元素的生效样式
getType：返回值或变量的类型名
hasClass：校验指定元素的类名
hide：隐藏所有的指定标签
httpsRedirect：HTTP 跳转 HTTPS
insertAfter：在指定元素之后插入新元素
insertBefore：在指定元素之前插入新元素
isBrowser：检查是否为浏览器环境
isBrowserTab：检查当前标签页是否活动
nodeListToArray：转换nodeList为数组
Random Hexadecimal Color Code：随机十六进制颜色
scrollToTop：平滑滚动至顶部
smoothScroll：滚动到指定元素区域
detectDeviceType：检测移动/PC设备
getScrollPosition：返回当前的滚动位置
size：获取不同类型变量的字节长度
escapeHTML：转义HTML
calc:js计算精确度丢失问题解决
getSelected：选中屏蔽点击事件
getURL：获取html中路由传过来的参数
getNowFormatDate：获取当前时间（ 年-月-日 时：分：秒）
timestampToTime：时间戳转化成日期格式
tranNumber：数字转整数 如 100000 转为10万
numberFormat：数字转整数 如 100000 转为10万
formatNum1:将一串随机数字每四位加一个自定义符号（格式：1234-5678-90）
formatNum2:将一串随机数字每四位加一个自定义符号（格式：1234-5678-90）
concatArray：将多维数组转成一维数组
bing：求两个数组的并集
jiao：求两个数组的交集
cha：求两个数组的差集
objectIsAllNull：判断一个对象是否有空值
insert_flg：往指定位置插入字符
clickUp:点击上移
clickDown:点击下移
swapArray：数组元素互换位置
*/



// let timer;
// function throttle(){
//     clearTimeout(timer);
//     timer = setTimeout(() => {}, 2000);

// }
// let bool=true;
// function debounce() {
//     if (bool) {
//         bool = false;
//         setTimeout(()=>{
//             bool = true;
//         },2000)
//     }
// }

class Tool {
    constructor() {

    }
    //精简的节流函数
    //时间戳版
    throttleT(func, wait) {
        let previous = 0;
        return function () {
            let now = Date.now();
            let context = this;
            let args = arguments;
            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }
    }
    // throttle(count,1000);
    //定时器版
    throttleD(func, wait) {
        let timeout;
        return function () {
            let context = this;
            let args = arguments;
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
        }
    }
    //精简的防抖函数
    //时间戳版
    debounceT(fn, delay = 200) {
        let timer = null;
        return function () {
            if (timer) clearTimeout(timer);
            timer = setTimeout(() => {
                fn.apply(this, arguments);
                timer = null;
            }, delay);
        }
    }
    //定时器版
    debounceD(func, wait) {
        let timeout;
        return function () {
            let context = this;
            let args = arguments;
            if (timeout) clearTimeout(timeout);
            timeout = setTimeout(() => {
                func.apply(context, args)
            }, wait);
        }
    }
    //排序（对于json中的某个字段进行排序）
    compare(property) {
        return function (a, b) {
            var value1 = a[property];
            var value2 = b[property];
            return value1 - value2;
        }
    }
    //用法 console.log(res.data.data.sort(compare('property')))
    delObjectNull(obj) {
        /*******删除json中空的字段 */
        //////*****用法    filterFalse(obj)        ******************* */
        if (isArrayFn(obj)) return;
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                if (!obj[key]) {
                    delete obj[key];
                    continue
                } else if (typeof obj[key] === "object") {
                    filterFalse(obj[key])
                    if (isEmpty(obj[key])) {
                        delete obj[key]
                    }
                }
            }
        }
        function isEmpty(obj) {
            var key;
            for (key in obj) {
                if (obj.hasOwnProperty(key)) {
                    return false;
                }
            }
            return true;
        }
        function isArrayFn(value) {
            if (typeof Array.isArray === "function") {
                return Array.isArray(value);
            } else {
                return Object.prototype.toString.call(value) === "[object Array]";
            }
        }
    }

    /*******************根据省份代码找到省份名称************** */

    //   const options = [
    //     {
    //       "value":'',
    //       "label":'不限',
    //       "children":[],
    //     },
    //     {
    //       "value": "11",
    //       "label": "北京市",
    //       "children": [
    //         {
    //           "value": "",
    //           "label": "不限"
    //         },
    //         {
    //           "value": "01",
    //           "label": "北京城区",
    //           "children": [
    //             {
    //               "value": "",
    //               "label": "不限"
    //             },
    //             {
    //               "value": "01",
    //               "label": "东城区"
    //             }
    //         }]
    cal_city(value) {//处理省份的
        if (value) {
            var data = reg.filter((kv) => {
                return kv['value'] === value
            });
            if (data === []) {
                return [];
            } else {
                return data[0];
            }
        }
    }
    cal_citytwo(array, value) {//处理省份下的地区，城市的
        if (array && value) {
            var data = array.filter((kv) => {
                return kv['value'] === value
            });
            if (data === []) {
                return {};
            } else {
                return data[0];
            }
        }
    }
    //函数的防抖
    debounce(fn, time) {
        let timeout;
        return function () {
            const functionCall = () => fn.apply(this, arguments);
            clearTimeout(timeout);
            timeout = setTimeout(functionCall, time);
        }
    }
    // 案例
    // 先在需要的地方引入js方法包
    //import debounce from './utils'
    //在页面上进入标签写方法
    //<input type="text" @input="debounceInput($event)">
    // export default {
    //   methods: {
    //     debounceInput(E){
    //       debounce(() => {
    //         console.log(E.target.value)
    //       }, 1000)
    //     }
    //   }
    // }
    // 函数的节流
    throttle(func, interval) {
        let timeout;
        let startTime = new Data();
        return function () {
            clearTimeout(timeout);
            let curTime = new Date();
            if (curTime - startTime <= interval) {
                //小于规定时间间隔时，用setTimeout在指定时间后再执行
                timeout = setTimeout(() => {
                    func();
                }, interval)
            } else {
                //重新计时并执行函数
                startTime = curTime;
                func()
            }
        }
    }
    //===============================处理时间的函数封装==================================//
    //判断某年份是否为闰年
    isLeapYear(year) {
        return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
    }


    //将日期格式化输出 “2015-08-24”
    date2string(date, sep) {
        var sep = sep || "-";
        var m = date.getMonth() + 1;
        var d = date.getDate();
        return date.getFullYear() + sep + (m < 10 ? "0" + m : m) + sep + (d < 10 ? "0" + d : d);
    }

    // 使用说明 date2string(new Date())

    //获得某个月份的天数
    getDaysByMonth(month, year) {
        year = year || new Date().getFullYear();

        if (!month || typeof month != "number") {
            console.error("参数必须为数字类型！");
            return;
        }
        if (!(month > 0 && month < 13)) {
            console.error("月份必须在1-12之间");
            return;
        }
        month = Math.round(month);

        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12: return 31;
            case 2: return (isLeapYear(year) ? 29 : 28);
            case 4:
            case 6:
            case 9:
            case 11: return 30;
        }
    }

    //将字符串转换为日期
    string2Date(datestr, sep) {
        if (!sep || !(datestr && datestr.length >= 8)) {
            console.error("字符串格式错误！不能解析");
            return;
        }

        var list = datestr.split(sep);
        if (!(list[0].length == 4 && list[1] > 0 && list[1] < 13 && list[2] > 0 && list[2] < 32)) {
            console.error("字符串格式错误！不能解析");
            return;
        }
        return new Date(datestr);
    }


    //判断两个日期相差的天数
    getDaysBetweenMonths(d1, d2) {
        if (!(d1 instanceof Date && d2 instanceof Date)) {
            console.error("参数传错了！重来！");
            return;
        }
        var dis = Math.abs(d1.getTime() - d2.getTime());
        return (dis / 1000 / 3600 / 24).toFixed(2);
    }


    //获得N天以后的日期(string/date)
    getAfterDay(n) {
        var now = new Date();
        now.setDate(now.getDate() + n);
        return date2string(now);
    }

    //实现一键复制函数
    //type是要复制内容标签的类型，text是要复制的内容属性名，msg是提示内容
    copy1(type, text, msg) {
        if (type == "id") {
            var Url2 = document.querySelector("#" + text)
        }
        if (type == "class") {
            var Url2 = document.querySelector("." + text)
        }
        Url2.select(); // 选中对象
        document.execCommand("Copy"); // 执行浏览器复制命令
        alert(msg);
    }
    //关于句柄的事件方法开始
    // 添加句柄
    addHandler(element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent('on' + type, handler);
        } else {
            element['on' + type] = handler;
        }
    }
    // 删除句柄
    removeHandler(element, type, handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent('on' + type, handler);
        } else {
            element['on' + type] = null;
        }
    }
    getEvent(event) {
        return event ? event : window.event;
    }
    getType(event) {
        return event.type;
    }
    getElement(event) {
        return event.target || event.srcElement;
    }
    preventDefault(event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    }
    stopPropagation(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    }
    //关于句柄的事件方法结束
    //保留汉字
    toCN(value) {
        var regEx = /[^\u4e00-\u9fa5\uf900-\ufa2d]/g;
        return value.replace(regEx, '');
    }

    //字符串截取后面加入...
    interceptString(string, len) {
        if (string.length > len) {
            return string.substring(0, len) + "...";
        } else {
            return string;
        }
    }

    //将一个字符串用给定的字符变成数组
    toArray(string, tag) {
        if (string.indexOf(tag) != -1) {
            return string.split(tag);
        } else {
            if (string != '') {
                return [string.toString()];
            } else {
                return [];
            }
        }
    }
    //只留下数字(0123456789)
    toNumber(value) {
        return value.replace(/\D/g, "");
    }

    //是否是以XX开头
    startsWith(value, tag) {
        return value.substring(0, tag.length) == tag;
    }
    //是否已XX结尾
    endWith(value, tag) {
        return value.substring(value.length - tag.length) == tag;
    }

    ///////Array工具类////
    /********************** Arry ***************/
    //根据数据取得再数组中的索引
    getIndex(array, obj) {
        for (var i = 0; i < array.length; i++) {
            if (obj == array[i]) {
                return i;
            }
        }
        return -1;
    }
    //移除数组中的某元素
    remove1(array, obj) {
        for (var i = 0; i < array.length; i++) {
            if (obj == array[i]) {
                array.splice(i, 1);
                break;
            }
        }
        return array;
    }
    //判断元素是否在数组中
    contains(array, obj) {
        for (var i = 0; i < array.length; i++) {
            if (obj == array[i]) {
                return true;
            }
        }
        return false;
    }
    //删除数组中的某个值并返回新的数组（1）
    remove2(arr, item) {
        var newarr = [];
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] != item) {
                newarr.push(arr[i]);
            }
        }
        return newarr;
    }
    //删除数组中的某个值并返回新的数组（2）
    remove3(arr, item) {
        var newarr = arr.slice(0);
        for (var i = 0; i < newarr.length; i++) {
            if (newarr[i] == item) {
                newarr.splice(i, 1);
                i--;
            }
        }
        return newarr;
    }
    //删除数组中的某个值并返回新的数组（3）
    //filter()进行过滤满足的数组属性将保留，并返回一个新数组
    remove4(arr, item) {
        return arr.filter(function (ele) {
            return ele != item;
        })
    }
    /********************** date工具类处理时间 ***************/
    format(format) {
        var o = {
            "M+": this.getMonth() + 1, //month
            "d+": this.getDate(), //day
            "h+": this.getHours(), //hour
            "m+": this.getMinutes(), //minute
            "s+": this.getSeconds(), //second
            "q+": Math.floor((this.getMonth() + 3) / 3), //quarter
            "S": this.getMilliseconds() //millisecond
        }
        if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        for (var k in o) if (new RegExp("(" + k + ")").test(format))
            format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
        return format;
    };

    //数组去重
    uniq(array) {
        var temp = []; //一个新的临时数组
        for (var i = 0; i < array.length; i++) {
            if (temp.indexOf(array[i]) == -1) {
                temp.push(array[i]);
            }
        }
        return temp;
    }
    //移除某个字段
    remove5(arr, item) {
        return arr.filter(function (ele) {
            return ele != item;
        })
    }


    //正则验证字母和数字
    checkRate(nubmer) {
        number_2 = nubmer.replace(/[(|)|（|）]/g, "");
        number_2 = number_2.replace(/(^\s*)|(\s*$)/g, "");
        var re = /^[0-9a-zA-Z]*$/g;  //判断字符串是否为数字和字母（）()组合     //判断正整数 /^[1-9]+[0-9]*]*$/
        if (!re.test(number_2)) {
            return false;
        } else {
            return true;
        }
    }

    //正则验证是否全汉字
    load(str) {
        str_2 = str.replace(/[(|)|（|）]/g, "");
        str_2 = str_2.replace(/(^\s*)|(\s*$)/g, "");
        var regex = /^[\u4E00-\u9FA5]+$/;
        if (!regex.test(str_2)) {
            return false;
        } else {
            return true;
        }
    }
    //===================================== 敏感词提示并替换===============================//

    // arr 是您的敏感词数组
    // value  是您要检测的内容
    filtion(arr, value) {
        let re = '';
        for (let i = 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                re += arr[i];
            } else {
                re += arr[i] + "|";
            }
            //定义正则表示式对象
            //利用RegExp可以动态生成正则表示式
            let r = new RegExp(arr[i], "ig");
            if (r.test(value)) {
                value = value.replace(r, "<font color='red'>" + arr[i] + "</font>");
                return {
                    key: false,
                    value: value
                };//这里是有敏感词的情况下，已经标红，并且提示为false，可以用来提示用户您有敏感词
            } else {
                return {
                    key: true,
                    value: value
                };//这里是没有敏感词的情况下，直接返回value内容，并且提示为true
            }
        }
    }
    //返回出敏感词
    forbiddenAry(stopWord, str) {
        var re = [];
        for (var i = 0; i < stopWord.length; i++) {
            if (i == stopWord.length - 1)
                re += stopWord[i];
            else
                re += stopWord[i] + "|";
        }
        var pattern = new RegExp(re, "g");
        var b = str.match(pattern)
        return b;
    }

    //一键复制
    /*
    ele:要拷贝的元素标签名
    btn:触发事件的按钮
    */
    copy2(ele, btn) {
        let COPY = document.getElementById(ele) || document.querySelector("." + ele);
        let BTN = document.getElementById(btn) || document.querySelector("." + btn);
        BTN.onclick = () => {
            COPY.select()
            document.execCommand("Copy")
        }
    }
    // 一. 第一部分：数组
    // 1. all：布尔全等判断
    all(arr, fn = Boolean) { arr.every(fn) }
    // all([4, 2, 3], x => x > 1); // true
    // all([1, 2, 3]); // true

    // 2. allEqual：检查数组各项相等
    allEqual(arr) { arr.every(val => val === arr[0]) }
    // allEqual([1, 2, 3, 4, 5, 6]); // false
    // allEqual([1, 1, 1, 1]); // true

    // 3. approximatelyEqual：约等于
    approximatelyEqual(v1, v2, epsilon = 0.001) { Math.abs(v1 - v2) < epsilon }
    // approximatelyEqual(Math.PI / 2.0, 1.5708); // true

    // 4. arrayToCSV：数组转CSV格式（带空格的字符串）
    arrayToCSV(arr, delimiter = ',') {
        arr.map(v => v.map(x => `"${x}"`).join(delimiter)).join('\n')
    }
    // arrayToCSV([['a', 'b'], ['c', 'd']]); // '"a","b"\n"c","d"'
    // arrayToCSV([['a', 'b'], ['c', 'd']], ';'); // '"a";"b"\n"c";"d"'

    // 5. arrayToHtmlList：数组转li列表
    // 此代码段将数组的元素转换为<li>标签，并将其附加到给定ID的列表中。
    arrayToHtmlList(arr, listID) {
        (el => (
            (el = document.querySelector('#' + listID)),
            (el.innerHTML += arr.map(item => `<li>${item}</li>`).join(''))
        ))()
    }
    // arrayToHtmlList(['item 1', 'item 2'], 'myListID');

    // 6. average：平均数
    average(...nums) { nums.reduce((acc, val) => acc + val, 0) / nums.length }
    // average(...[1, 2, 3]); // 2
    // average(1, 2, 3); // 2

    // 7. averageBy：数组对象属性平均数
    // 此代码段将获取数组对象属性的平均值
    averageBy(arr, fn) {
        arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) /
            arr.length
    }
    // averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 5
    // averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 5

    //8. bifurcate：拆分断言后的数组
    // 可以根据每个元素返回的值，使用reduce()和push() 将元素添加到第二次参数fn中 。
    bifurcate(arr, filter) {
        arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []])
    }
    // bifurcate(['beep', 'boop', 'foo', 'bar'], [true, true, false, true]); 
    // [ ['beep', 'boop', 'bar'], ['foo'] ]

    //9. castArray：其它类型转数组
    castArray(val) { (Array.isArray(val) ? val : [val]) }
    // castArray('foo'); // ['foo']
    // castArray([1]); // [1]
    // castArray(1); // [1]

    //10. compact：去除数组中的无效/无用值
    compact(arr) { arr.filter(Boolean) }
    // compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]); 
    // [ 1, 2, 3, 'a', 's', 34 ]

    //11. countOccurrences：检测数值出现次数
    countOccurrences(arr, val) { arr.reduce((a, v) => (v === val ? a + 1 : a), 0) }
    // countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3

    //12. deepFlatten：递归扁平化数组
    deepFlatten(arr) { [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v))) }
    // deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]

    //13. difference：寻找差异（并返回第一个数组独有的）
    // 此代码段查找两个数组之间的差异，并返回第一个数组独有的。
    difference(a, b) {
        const s = new Set(b);
        return a.filter(x => !s.has(x));
    };
    // difference([1, 2, 3], [1, 2, 4]); // [3]

    //14. differenceBy：先执行再寻找差异
    // 在将给定函数应用于两个列表的每个元素之后，此方法返回两个数组之间的差异。
    differenceBy(a, b, fn) {
        const s = new Set(b.map(fn));
        return a.filter(x => !s.has(fn(x)));
    };
    // differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [1.2]
    // differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], v => v.x); // [ { x: 2 } ]

    //15. dropWhile：删除不符合条件的值
    // 此代码段从数组顶部开始删除元素，直到传递的函数返回为true。
    dropWhile(arr, func) {
        while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
        return arr;
    };
    // dropWhile([1, 2, 3, 4], n => n >= 3); // [3,4]

    //16. flatten：指定深度扁平化数组
    // 此代码段第二参数可指定深度。
    flatten(arr, depth = 1) {
        arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), [])
    }
    // flatten([1, [2], 3, 4]); // [1, 2, 3, 4]
    // flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8]

    //17. indexOfAll：返回数组中某值的所有索引
    // 此代码段可用于获取数组中某个值的所有索引，如果此值中未包含该值，则返回一个空数组。
    indexOfAll(arr, val) { arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []) }
    // indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
    // indexOfAll([1, 2, 3], 4); // []

    //18. intersection：两数组的交集
    intersection(a, b) {
        const s = new Set(b);
        return a.filter(x => s.has(x));
    };
    // intersection([1, 2, 3], [4, 3, 2]); // [2, 3]

    //19. intersectionWith：两数组都符合条件的交集
    // 此片段可用于在对两个数组的每个元素执行了函数之后，返回两个数组中存在的元素列表。
    intersectionBy(a, b, fn) {
        const s = new Set(b.map(fn));
        return a.filter(x => s.has(fn(x)));
    };
    // intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [2.1]

    //20. intersectionWith：先比较后返回交集
    intersectionWith(a, b, comp) { a.filter(x => b.findIndex(y => comp(x, y)) !== -1) }
    // intersectionWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0, 3.9], (a, b) => Math.round(a) === Math.round(b)); // [1.5, 3, 0]

    //21. minN：返回指定长度的升序数组
    minN(arr, n = 1) { [...arr].sort((a, b) => a - b).slice(0, n) }
    // minN([1, 2, 3]); // [1]
    // minN([1, 2, 3], 2); // [1,2]

    //22. negate：根据条件反向筛选
    negate(func) { (...args) => !func(...args) }
    // [1, 2, 3, 4, 5, 6].filter(negate(n => n % 2 === 0)); // [ 1, 3, 5 ]

    //23. randomIntArrayInRange：生成两数之间指定长度的随机数组
    randomIntArrayInRange(min, max, n = 1) {
        Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min)
    }
    // randomIntArrayInRange(12, 35, 10); // [ 34, 14, 27, 17, 30, 27, 20, 26, 21, 14 ]

    //24. sample：在指定数组中获取随机数
    sample(arr) { arr[Math.floor(Math.random() * arr.length)] }
    // sample([3, 7, 9, 11]); // 9
    //25. sampleSize：在指定数组中获取指定长度的随机数
    // 此代码段可用于从数组中获取指定长度的随机数，直至穷尽数组。
    // 使用Fisher-Yates算法对数组中的元素进行随机选择。
    sampleSize([...arr], n = 1) {
        let m = arr.length;
        while (m) {
            const i = Math.floor(Math.random() * m--);
            [arr[m], arr[i]] = [arr[i], arr[m]];
        }
        return arr.slice(0, n);
    };
    // sampleSize([1, 2, 3], 2); // [3,1]
    // sampleSize([1, 2, 3], 4); // [2,3,1]
    // 获取两数之间的随机数
    getRandomInt(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min)) + min; //不含最大值，含最小值
    }
    //26. shuffle：“洗牌” 数组
    // 此代码段使用Fisher-Yates算法随机排序数组的元素。
    shuffle([...arr]) {
        let m = arr.length;
        while (m) {
            const i = Math.floor(Math.random() * m--);
            [arr[m], arr[i]] = [arr[i], arr[m]];
        }
        return arr;
    };
    // const foo = [1, 2, 3];
    // shuffle(foo); // [2, 3, 1], foo = [1, 2, 3]

    //--------------------------第二部分：函数-------------------------------------//


    // 2. defer：推迟执行
    // 此代码段延迟了函数的执行，直到清除了当前调用堆栈。
    defer(fn, ...args) { setTimeout(fn, 1, ...args) }
    // defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a'

    // 3. runPromisesInSeries：运行多个Promises
    runPromisesInSeries(ps) { ps.reduce((p, next) => p.then(next), Promise.resolve()) }
    // const delay = d => new Promise(r => setTimeout(r, d));
    // runPromisesInSeries([() => delay(1000), () => delay(2000)]);
    //依次执行每个Promises ，总共需要3秒钟才能完成

    // 4. timeTaken：计算函数执行时间
    timeTaken(callback) {
        //   console.time('timeTaken');
        const r = callback();
        //   console.timeEnd('timeTaken');
        return r;
    };
    // timeTaken(() => Math.pow(2, 10)); // 1024, (logged): timeTaken: 0.02099609375ms
    // 5. createEventHub：简单的发布/订阅模式
    // 创建一个发布/订阅（发布-订阅）事件集线，有emit，on和off方法。
    // 使用Object.create(null)创建一个空的hub对象。
    // emit，根据event参数解析处理程序数组，然后.forEach()通过传入数据作为参数来运行每个处理程序。
    // on，为事件创建一个数组（若不存在则为空数组），然后.push()将处理程序添加到该数组。
    // off，用.findIndex()在事件数组中查找处理程序的索引，并使用.splice()删除。
    createEventHub() {
        ({
            hub: Object.create(null),
            emit(event, data) {
                (this.hub[event] || []).forEach(handler => handler(data));
            },
            on(event, handler) {
                if (!this.hub[event]) this.hub[event] = [];
                this.hub[event].push(handler);
            },
            off(event, handler) {
                const i = (this.hub[event] || []).findIndex(h => h === handler);
                if (i > -1) this.hub[event].splice(i, 1);
                if (this.hub[event].length === 0) delete this.hub[event];
            }
        })
    }
    /*
    // 用法：
    const handler = data => console.log(data);
    const hub = createEventHub();
    let increment = 0;
    // 订阅，监听不同事件
    hub.on('message', handler);
    hub.on('message', () => console.log('Message event fired'));
    hub.on('increment', () => increment++);
    // 发布：发出事件以调用所有订阅给它们的处理程序，并将数据作为参数传递给它们
    hub.emit('message', 'hello world'); // 打印 'hello world' 和 'Message event fired'
    hub.emit('message', { hello: 'world' }); // 打印 对象 和 'Message event fired'
    hub.emit('increment'); // increment = 1
    // 停止订阅
    hub.off('message', handler);
    */



    // 6. memoize：缓存函数
    // 通过实例化一个Map对象来创建一个空的缓存。
    // 通过检查输入值的函数输出是否已缓存，返回存储一个参数的函数，该参数将被提供给已记忆的函数；如果没有，则存储并返回它。
    memoize(fn) {
        const cache = new Map();
        const cached = function (val) {
            return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);
        };
        cached.cache = cache;
        return cached;
    };


    // 7. once：只调用一次的函数
    once(fn) {
        let called = false
        return function () {
            if (!called) {
                called = true
                fn.apply(this, arguments)
            }
        }
    };

    // 8. flattenObject：以键的路径扁平化对象
    // 使用递归。
    // 利用Object.keys(obj)联合Array.prototype.reduce()，以每片叶子节点转换为扁平的路径节点。
    // 如果键的值是一个对象，则函数使用调用适当的自身prefix以创建路径Object.assign()。
    // 否则，它将适当的前缀键值对添加到累加器对象。
    // prefix除非您希望每个键都有一个前缀，否则应始终省略第二个参数。
    flattenObject(obj, prefix = '') {
        Object.keys(obj).reduce((acc, k) => {
            const pre = prefix.length ? prefix + '.' : '';
            if (typeof obj[k] === 'object') Object.assign(acc, flattenObject(obj[k], pre + k));
            else acc[pre + k] = obj[k];
            return acc;
        }, {})
    }
    // flattenObject({ a: { b: { c: 1 } }, d: 1 }); // { 'a.b.c': 1, d: 1 }

    // 9. unflattenObject：以键的路径展开对象与上面的相反，展开对象。
    unflattenObject(obj) {
        Object.keys(obj).reduce((acc, k) => {
            if (k.indexOf('.') !== -1) {
                const keys = k.split('.');
                Object.assign(
                    acc,
                    JSON.parse(
                        '{' +
                        keys.map((v, i) => (i !== keys.length - 1 ? `"${v}":{` : `"${v}":`)).join('') +
                        obj[k] +
                        '}'.repeat(keys.length)
                    )
                );
            } else acc[k] = obj[k];
            return acc;
        }, {})
    }
    // unflattenObject({ 'a.b.c': 1, d: 1 }); // { a: { b: { c: 1 } }, d: 1 }
    /*=================================第三部分：字符串=================================================*/

    // 1.byteSize：返回字符串的字节长度
    byteSize(str) { new Blob([str]).size }
    // byteSize('😀'); // 4
    // byteSize('Hello World'); // 11

    // 2. capitalize：首字母大写
    capitalize([first, ...rest]) {
        first.toUpperCase() + rest.join('')
    }
    // capitalize('fooBar'); // 'FooBar'
    // capitalize('fooBar', true); // 'Foobar'

    // 3. capitalizeEveryWord：每个单词首字母大写
    capitalizeEveryWord(str) { str.replace(/\b[a-z]/g, char => char.toUpperCase()) }
    // capitalizeEveryWord('hello world!'); // 'Hello World!'

    // 4. decapitalize：首字母小写
    decapitalize([first, ...rest]) {
        first.toLowerCase() + rest.join('')
    }
    // decapitalize('FooBar'); // 'fooBar'
    // decapitalize('FooBar'); // 'fooBar'

    // 5. luhnCheck：银行卡号码校验（luhn算法）
    // Luhn算法的实现，用于验证各种标识号，例如信用卡号，IMEI号，国家提供商标识号等。
    // 与String.prototype.split('')结合使用，以获取数字数组。获得最后一个数字。实施luhn算法。如果被整除，则返回，否则返回。
    luhnCheck(num) {
        let arr = (num + '')
            .split('')
            .reverse()
            .map(x => parseInt(x));
        let lastDigit = arr.splice(0, 1)[0];
        let sum = arr.reduce((acc, val, i) => (i % 2 !== 0 ? acc + val : acc + ((val * 2) % 9) || 9), 0);
        sum += lastDigit;
        return sum % 10 === 0;
    };
    // 用例:
    // luhnCheck('4485275742308327'); // true
    // luhnCheck(6011329933655299); //  false
    // luhnCheck(123456789); // false


    // 6. splitLines：将多行字符串拆分为行数组。
    // 使用String.prototype.split()和正则表达式匹配换行符并创建一个数组。
    splitLines(str) { str.split(/\r?\n/) }
    // splitLines('This\nis a\nmultiline\nstring.\n'); // ['This', 'is a', 'multiline', 'string.' , '']


    // 7. stripHTMLTags：删除字符串中的HTMl标签
    // 从字符串中删除HTML / XML标签。
    // 使用正则表达式从字符串中删除HTML / XML 标记。
    stripHTMLTags(str) { str.replace(/<[^>]*>/g, '') }
    // stripHTMLTags('<p><em>lorem</em> <strong>ipsum</strong></p>'); // 'lorem ipsum'
    /*===============================4. 第四部分：对象=====================================*/


    // 1. dayOfYear：当前日期天数
    dayOfYear(date) {
        Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24)
    }
    // dayOfYear(new Date()); // 285


    // 2. forOwn：迭代属性并执行回调
    forOwn(obj, fn) { Object.keys(obj).forEach(key => fn(obj[key], key, obj)) }
    // forOwn({ foo: 'bar', a: 1 }, v => console.log(v)); // 'bar', 1


    // 3. Get Time From Date：返回当前24小时制时间的字符串
    getColonTimeFromDate(date) { date.toTimeString().slice(0, 8) }
    // getColonTimeFromDate(new Date()); // "08:38:00"


    // 4. Get Days Between Dates：返回日期间的天数
    getDaysDiffBetweenDates(dateInitial, dateFinal) {
        (dateFinal - dateInitial) / (1000 * 3600 * 24)
    }
    // getDaysDiffBetweenDates(new Date('2019-01-01'), new Date('2019-10-14')); // 286


    // 5. is：检查值是否为特定类型。
    is(type, val) { ![, null].includes(val) && val.constructor === type }
    // is(Array, [1]); // true
    // is(ArrayBuffer, new ArrayBuffer()); // true
    // is(Map, new Map()); // true
    // is(RegExp, /./g); // true
    // is(Set, new Set()); // true
    // is(WeakMap, new WeakMap()); // true
    // is(WeakSet, new WeakSet()); // true
    // is(String, ''); // true
    // is(String, new String('')); // true
    // is(Number, 1); // true
    // is(Number, new Number(1)); // true
    // is(Boolean, true); // true
    // is(Boolean, new Boolean(true)); // true


    // 6. isAfterDate：检查是否在某日期后
    isAfterDate(dateA, dateB) { dateA > dateB }
    // isAfterDate(new Date(2010, 10, 21), new Date(2010, 10, 20)); // true


    // 7. isBeforeDate：检查是否在某日期前
    isBeforeDate(dateA, dateB) { dateA < dateB }
    // isBeforeDate(new Date(2010, 10, 20), new Date(2010, 10, 21)); // true


    // 8 tomorrow：获取明天的字符串格式时间
    tomorrow() {
        let t = new Date();
        t.setDate(t.getDate() + 1);
        return t.toISOString().split('T')[0];
    };
    // tomorrow(); // 2019-10-15 (如果明天是2019-10-15)


    // 9. equals：全等判断
    // 在两个变量之间进行深度比较以确定它们是否全等。
    // 此代码段精简的核心在于Array.prototype.every()的使用。
    equals(a, b) {
        if (a === b) return true;
        if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
        if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) return a === b;
        if (a.prototype !== b.prototype) return false;
        let keys = Object.keys(a);
        if (keys.length !== Object.keys(b).length) return false;
        return keys.every(k => equals(a[k], b[k]));
    };
    // 用法：
    // equals({ a: [2, { e: 3 }], b: [4], c: 'foo' }, { a: [2, { e: 3 }], b: [4], c: 'foo' }); // true
    /*====================================5. 第五部分：数字=============================================*/



    // 1. randomIntegerInRange：生成指定范围的随机整数
    randomIntegerInRange(min, max) { Math.floor(Math.random() * (max - min + 1)) + min }
    // randomIntegerInRange(0, 5); // 3


    // 2. randomNumberInRange：生成指定范围的随机小数
    randomNumberInRange(min, max) { Math.random() * (max - min) + min }
    // randomNumberInRange(2, 10); // 6.0211363285087005


    // 3. round：四舍五入到指定位数
    round(n, decimals = 0) { Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`) }
    // round(1.005, 2); // 1.01


    // 4. sum：计算数组或多个数字的总和
    sum(...arr) { [...arr].reduce((acc, val) => acc + val, 0) }
    // sum(1, 2, 3, 4); // 10
    // sum(...[1, 2, 3, 4]); // 10


    // 5. toCurrency：简单的货币单位转换
    toCurrency(n, curr, LanguageFormat = undefined) { Intl.NumberFormat(LanguageFormat, { style: 'currency', currency: curr }).format(n) }
    // toCurrency(123456.789, 'EUR'); // €123,456.79
    // toCurrency(123456.789, 'USD', 'en-us'); // $123,456.79  
    // toCurrency(123456.789, 'USD', 'fa'); // ۱۲۳٬۴۵۶٫۷۹
    // toCurrency(322342436423.2435, 'JPY'); // ¥322,342,436,423 




    /*==============================6. 第六部分：浏览器操作及其它============================================*/


    // 1. bottomVisible：检查页面底部是否可见
    bottomVisible() {
        document.documentElement.clientHeight + window.scrollY >=
            (document.documentElement.scrollHeight || document.documentElement.clientHeight)
    }
    // bottomVisible(); // true


    // 2. Create Directory：检查创建目录
    // 此代码段调用fs模块的existsSync()检查目录是否存在，如果不存在，则mkdirSync()创建该目录。
    // const fs = require('fs');
    createDirIfNotExists(dir) { (!fs.existsSync(dir) ? fs.mkdirSync(dir) : undefined) }
    // createDirIfNotExists('test');

    // 3. currentURL：返回当前链接url
    currentURL() { window.location.href }
    // currentURL(); // 'https://juejin.im'


    // 4. distance：返回两点间的距离
    // 该代码段通过计算欧几里得距离来返回两点之间的距离。
    distance(x0, y0, x1, y1) { Math.hypot(x1 - x0, y1 - y0) }
    // distance(1, 1, 2, 3); // 2.23606797749979


    // 5. elementContains：检查是否包含子元素
    // 此代码段检查父元素是否包含子元素。
    elementContains(parent, child) { parent !== child && parent.contains(child) }
    // elementContains(document.querySelector('head'), document.querySelector('title')); // true
    // elementContains(document.querySelector('body'), document.querySelector('body')); // false


    // 6. getStyle：返回指定元素的生效样式
    getStyle(el, ruleName) { getComputedStyle(el)[ruleName] }
    // getStyle(document.querySelector('p'), 'font-size'); // '16px'


    // 7. getType：返回值或变量的类型名
    getType(v) { v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase() }
    // getType(new Set([1, 2, 3])); // 'set'
    // getType([1, 2, 3]); // 'array'


    // 8. hasClass：校验指定元素的类名
    hasClass(el, className) { el.classList.contains(className) }
    // hasClass(document.querySelector('p.special'), 'special'); // true


    // 9. hide：隐藏所有的指定标签
    hide(...el) { [...el].forEach(e => (e.style.display = 'none')) }
    // hide(document.querySelectorAll('img')); // 隐藏所有<img>标签


    // 10. httpsRedirect：HTTP 跳转 HTTPS
    httpsRedirect() {
        if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]);
    };
    // httpsRedirect(); // 若在`http://www.baidu.com`, 则跳转到`https://www.baidu.com`


    // 11. insertAfter：在指定元素之后插入新元素
    insertAfter(el, htmlString) { el.insertAdjacentHTML('afterend', htmlString) }
    // <div id="myId">...</div> <p>after</p>
    // insertAfter(document.getElementById('myId'), '<p>after</p>'); 


    // 12. insertBefore：在指定元素之前插入新元素
    insertBefore(el, htmlString) { el.insertAdjacentHTML('beforebegin', htmlString) }
    // insertBefore(document.getElementById('myId'), '<p>before</p>'); // <p>before</p> <div id="myId">...</div>


    // 13. isBrowser：检查是否为浏览器环境
    // 此代码段可用于确定当前运行时环境是否为浏览器。这有助于避免在服务器（节点）上运行前端模块时出错。
    isBrowser() { ![typeof window, typeof document].includes('undefined') }
    // isBrowser(); // true (browser)
    // isBrowser(); // false (Node)


    // 14. isBrowserTab：检查当前标签页是否活动
    isBrowserTabFocused() { !document.hidden; }
    // isBrowserTabFocused(); // true


    // 15. nodeListToArray：转换nodeList为数组
    nodeListToArray(nodeList) { [...nodeList] }
    // nodeListToArray(document.childNodes); // [ <!DOCTYPE html>, html ]


    // 16. Random Hexadecimal Color Code：随机十六进制颜色
    randomHexColorCode() {
        let n = (Math.random() * 0xfffff * 1000000).toString(16);
        return '#' + n.slice(0, 6);
    };
    // randomHexColorCode(); // "#e34155"


    // 17. scrollToTop：平滑滚动至顶部
    // 该代码段可用于平滑滚动到当前页面的顶部。
    scrollToTop() {
        const c = document.documentElement.scrollTop || document.body.scrollTop;
        if (c > 0) {
            window.requestAnimationFrame(scrollToTop);
            window.scrollTo(0, c - c / 8);
        }
    };
    // scrollToTop();


    // 18. smoothScroll：滚动到指定元素区域
    // 该代码段可将指定元素平滑滚动到浏览器窗口的可见区域。
    smoothScroll(element) {
        document.querySelector(element).scrollIntoView({
            behavior: 'smooth'
        })
    }
    // smoothScroll('#fooBar'); 
    // smoothScroll('.fooBar'); 
    // 19. detectDeviceType：检测移动/PC设备
    detectDeviceType() {
        /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
            ? 'Mobile'
            : 'Desktop'
    }


    // 20. getScrollPosition：返回当前的滚动位置
    // 默认参数为window ，pageXOffset(pageYOffset)为第一选择，没有则用scrollLeft(scrollTop)
    getScrollPosition(el = window) {
        ({
            x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
            y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
        })
    }
    // getScrollPosition(); // {x: 0, y: 200}


    // 21. size：获取不同类型变量的字节长度
    // 这个的实现非常巧妙，利用Blob类文件对象的特性，获取对象的长度。
    // 另外，多重三元运算符，是真香。
    size(val) {
        Array.isArray(val)
            ? val.length
            : val && typeof val === 'object'
                ? val.size || val.length || Object.keys(val).length
                : typeof val === 'string'
                    ? new Blob([val]).size
                    : 0
    }
    // size([1, 2, 3, 4, 5]); // 5
    // size('size'); // 4
    // size({ one: 1, two: 2, three: 3 }); // 3



    // 22. escapeHTML：转义HTML
    // 当然是用来防XSS攻击啦。
    escapeHTML(str) {
        str.replace(
            /[&<>'"]/g,
            tag =>
                ({
                    '&': '&amp;',
                    '<': '&lt;',
                    '>': '&gt;',
                    "'": '&#39;',
                    '"': '&quot;'
                }[tag] || tag)
        )
    }
    // escapeHTML('<a href="#">Me & you</a>'); // '&lt;a href=&quot;#&quot;&gt;Me &amp; you&lt;/a&gt;'




    /*
    函数，加法函数，用来得到精确的加法结果 
    说明：javascript的加法结果会有误差，在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
    参数：arg1：第一个加数；arg2第二个加数；d要保留的小数位数（可以不传此参数，如果不传则不处理小数位数）
    调用：Calc.Add(arg1,arg2,d) 
    返回值：两数相加的结果
    */
    Add(arg1, arg2) {
        arg1 = arg1.toString(), arg2 = arg2.toString();
        var arg1Arr = arg1.split("."), arg2Arr = arg2.split("."), d1 = arg1Arr.length == 2 ? arg1Arr[1] : "", d2 = arg2Arr.length == 2 ? arg2Arr[1] : "";
        var maxLen = Math.max(d1.length, d2.length);
        var m = Math.pow(10, maxLen);
        var result = Number(((arg1 * m + arg2 * m) / m).toFixed(maxLen));
        var d = arguments[2];
        return typeof d === "number" ? Number((result).toFixed(d)) : result;
    };
    /*
    函数：减法函数，用来得到精确的减法结果 
    说明：函数返回较为精确的减法结果。
    参数：arg1：第一个加数；arg2第二个加数；d要保留的小数位数（可以不传此参数，如果不传则不处理小数位数
    调用：Calc.Sub(arg1,arg2) 
    返回值：两数相减的结果
    */
    Sub(arg1, arg2) {
        return Calc.Add(arg1, -Number(arg2), arguments[2]);
    };
    /*
    函数：乘法函数，用来得到精确的乘法结果 
    说明：函数返回较为精确的乘法结果。
    参数：arg1：第一个乘数；arg2第二个乘数；d要保留的小数位数（可以不传此参数，如果不传则不处理小数位数)
    调用：Calc.Mul(arg1,arg2) 
    返回值：两数相乘的结果
    */
    Mul(arg1, arg2) {
        var r1 = arg1.toString(), r2 = arg2.toString(), m, resultVal, d = arguments[2];
        m = (r1.split(".")[1] ? r1.split(".")[1].length : 0) + (r2.split(".")[1] ? r2.split(".")[1].length : 0);
        resultVal = Number(r1.replace(".", "")) * Number(r2.replace(".", "")) / Math.pow(10, m);
        return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
    };
    /*
    函数：除法函数，用来得到精确的除法结果 
    说明：函数返回较为精确的除法结果。
    参数：arg1：除数；arg2被除数；d要保留的小数位数（可以不传此参数，如果不传则不处理小数位数)
    调用：Calc.Div(arg1,arg2) 
    返回值：arg1除于arg2的结果
    */
    Div(arg1, arg2) {
        var r1 = arg1.toString(), r2 = arg2.toString(), m, resultVal, d = arguments[2];
        m = (r2.split(".")[1] ? r2.split(".")[1].length : 0) - (r1.split(".")[1] ? r1.split(".")[1].length : 0);
        resultVal = Number(r1.replace(".", "")) / Number(r2.replace(".", "")) * Math.pow(10, m);
        return typeof d !== "number" ? Number(resultVal) : Number(resultVal.toFixed(parseInt(d)));
    }

    //选中屏蔽点击事件
    getSelected() {
        if (window.getSelection) {
            return window.getSelection().toString();
        } else if (document.getSelection) {
            return document.getSelection().toString();
        } else {
            var selection = document.selection && document.selection.createRange();
            if (selection.text) {
                return selection.text.toString();
            }
            return "";
        }
    }

    //获取html中路由传过来的参数
    getURL() {
        var url = location.search; //获取url中"?"符后的字串
        if (url.indexOf("?") != -1) {
            var _URL = url.substr(1).split("&");
            var c = {};
            _URL.forEach(e => {
                var b = e.split("=");
                c[b[0]] = decodeURIComponent(b[1]);
            });
            return c;
        }
    }
    //获取当前时间（ 年-月-日 时：分：秒）
    getNowFormatDate() {//获取当前时间
        var date = new Date();
        var seperator1 = "-";
        var seperator2 = ":";
        var month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
        var strDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
        var currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate
            + " " + date.getHours() + seperator2 + date.getMinutes() + seperator2 + date.getSeconds();
        return currentdate;
    }


    //时间戳转化成日期格式
    timestampToTime(timestamp) {
        var date = new Date(timestamp * 1000);//时间戳为10位需*1000，时间戳为13位的话不需乘1000
        var Y = date.getFullYear() + '-';
        var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
        var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
        var h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
        var m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
        var s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
        return Y + M + D + h + m + s;
    }

    /**
            * 数字转整数 如 100000 转为10万
            * @param {需要转化的数} num 
            * @param {需要保留的小数位数} point 
            */
    tranNumber(num, point) {
        let numStr = num.toString()
        // 十万以内直接返回 
        if (numStr.length < 6) {
            return numStr;
        }
        //大于8位数是亿
        else if (numStr.length > 8) {
            let decimal = numStr.substring(numStr.length - 8, numStr.length - 8 + point);
            return parseFloat(parseInt(num / 100000000) + '.' + decimal) + '亿';
        }
        //大于6位数是十万 (以10W分割 10W以下全部显示)
        else if (numStr.length > 5) {
            let decimal = numStr.substring(numStr.length - 4, numStr.length - 4 + point)
            return parseFloat(parseInt(num / 10000) + '.' + decimal) + '万';
        }
    }

    numberFormat(value) {
        var param = {};
        var k = 10000,
            sizes = ['', '万', '亿', '万亿'],
            i;
        if (value < k) {
            param.value = value
            param.unit = ''
        } else {
            i = Math.floor(Math.log(value) / Math.log(k));

            param.value = ((value / Math.pow(k, i))).toFixed(2);
            param.unit = sizes[i];
        }
        return param;
    }

    //数字每三位加逗号
    formatNum(str) {
        var newStr = "";
        var count = 0;

        if (str.indexOf(".") == -1) {
            for (var i = str.length - 1; i >= 0; i--) {
                if (count % 3 == 0 && count != 0) {
                    newStr = str.charAt(i) + "," + newStr;
                } else {
                    newStr = str.charAt(i) + newStr;
                }
                count++;
            }
            str = newStr + ".00"; //自动补小数点后两位
        }
        else {
            for (var i = str.indexOf(".") - 1; i >= 0; i--) {
                if (count % 3 == 0 && count != 0) {
                    newStr = str.charAt(i) + "," + newStr;
                } else {
                    newStr = str.charAt(i) + newStr; //逐个字符相接起来
                }
                count++;
            }
            str = newStr + (str + "00").substr((str + "00").indexOf("."), 3);
        }
        return str;
    }


    //   将一串随机数字每四位加一个自定义符号（格式：1234-5678-90）
    // 方法一
    formatNum1(stringNum) {
        let saveString = "";
        saveString = stringNum.match(/\d{1,20}/g).join("|");
        return saveString;
    }
    //方法二
    formatNum2(string) {
        let result = '', index = 0;
        for (let i = 0; i < string.length; i++) {
            result += string.charAt(i);
            index++;
            if (index == 4 && (i + 1 != string.length)) {
                result += "-";
                index = 0;
            }
        }
        return result;
    }


    //拼接两个数组
    // （1）
    concatArray(a) {
        return a.reduce((res, current) => {
            return Array.isArray(current) ? [...res, ...toArray(current)] : [...res, current]
        }, [])
    }
    // （2）
    flat(arr) {
        var a = arr.reduce(function (acc, val) { return acc.concat(val) },
            []);
        return a;
    }

    bing(arr1, arr2) {
        //并集
        var bin = new Set([...arr1, ...arr2]);
        return [...bin];
    }

    jiao(arr1, arr2) {
        //交集
        var jiao = arr1.filter((val) => new Set(arr2).has(val));
        return jiao;
    }

    cha(arr1, arr2) {
        //差集
        var cha = arr1.filter((val) => !new Set(arr2).has(val));
        return cha;
    }

    objectIsAllNull(obj) {
        return Object.keys(obj).reduce((eb, e) =>eb || !obj[e], false)
    }

    //参数说明：str表示原字符串变量，flg表示要插入的字符串，sn表示要插入的位置
    insert_flg(str,flg,sn){
      var newstr="";
      for(var i=0;i<str.length;i+=sn){
        var tmp=str.substring(i, i+sn);
        newstr+=tmp+flg;
      }
      return newstr;
    }

    //点击上移
    clickUp(ARRAY,index){
        swapArray(ARRAY, index-1, index);
    }
    //点击下移
    clickDown(ARRAY,index){
        swapArray(ARRAY, index, index+1);
    }
    //数组元素互换位置
    swapArray(arr, index1, index2) {
        arr[index1] = arr.splice(index2, 1, arr[index1])[0];
        return arr;
    }

}



module.exports = Tool;